﻿#include "pch.h"
#if _QG_USE_D11
#include "qgdrv_d11_rdh.h"
#include "qgdrv_d11_cnv.h"

//
qgResObj* D11Rdh::_RoamInitVertexLayout(const qbLayoutArray& inz)
{
	static const char* sNames[] =
	{
		"POSITION",
		"COLOR",
		"BLENDWEIGHT",
		"BLENDINDICES",
		"NORMAL",
		"TEXCOORD",
		"TANGENT",
		"BINORMAL",
		"POSITION",
	};
	static DXGI_FORMAT sFmt[QGDLOT_MAX_VALUE] =
	{
		DXGI_FORMAT_R32_FLOAT,
		DXGI_FORMAT_R32G32_FLOAT,
		DXGI_FORMAT_R32G32B32_FLOAT,
		DXGI_FORMAT_R32G32B32A32_FLOAT,
		DXGI_FORMAT_R16G16_FLOAT,
		DXGI_FORMAT_R16G16B16A16_FLOAT,
		DXGI_FORMAT_R8G8_UINT,
		DXGI_FORMAT_R8G8B8A8_UINT,
		DXGI_FORMAT_R8G8B8A8_UNORM,
		DXGI_FORMAT_R16G16_SINT,
		DXGI_FORMAT_R16G16B16A16_SINT,
		DXGI_FORMAT_R16G16_SNORM,
		DXGI_FORMAT_R8G8B8A8_UNORM
	};

	//
	D11Layout* base = kobj_create(D11Layout);

	// 여기서는 목록만 만든다.
	// 왜냐하면 실제 InputLayout은 꼭지점 세이더의 코드데이터가 있어야함
	// 이유인즉슨 맞는지 확인(validation) 과정을 사전에 한번에 처리한다나 뭐래나...

	// 
	ksize_t i, cnt = inz.elms.size();
	kushort size, offset[QGSGVERT_MAX_VALUE] = {0, };
	auto& elas = base->_elas.elms;
	auto& descs = base->_descs;

	elas.resize(cnt);
	descs.resize(cnt);

	for (i = 0; i < cnt; i++)
	{
		D3D11_INPUT_ELEMENT_DESC& d = descs[i];
		const qbLayoutElement& e = inz.elms[i];

		size = (kushort)qgResSupp::LayoutTypeToSize(e.type);

		//
		d.SemanticName = sNames[e.usage];
		d.SemanticIndex = e.index;
		d.Format = sFmt[e.type];
		d.InputSlot = e.slot;
		d.AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;

		// [2014-1-1 KSH]
		// 아래 두개는 꼭지점 데이터냐, 인스턴스 데이터냐를 처리하는 건데
		// 입력 플래그를 받아 처리하도록 구조를 개선해야 함. 현재는 꼭지점 데이터만 처리
		// e.insts값에 인스턴스인지 아닌지 판단하는 방식을 고려할 것
		d.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
		d.InstanceDataStepRate = 0;

		// 오프셋 전진
		offset[e.slot] += size;

		//
		elas[i] = e;
	}

	// 0번의 AlignedByteOffset은 0으로 해야함
	descs[0].AlignedByteOffset = 0;

	// [2014-3-2 KIM] 값 
	for (base->_stride = i = 0; i < QGSGVERT_MAX_VALUE; i++)
	{
		base->_stride += offset[i];
		base->_stage[i] = offset[i];
	}

	return base;
}

//
qgResObj* D11Rdh::_RoamInitBuffer(kint type, kint stride, kint size, kint flags, kconstpointer data)
{
	D11Buffer* base = kobj_create(D11Buffer);

	//
	D3D11_BUFFER_DESC& bd = base->_bd;
	D3D11_SUBRESOURCE_DATA sd, *psd;

	bd.ByteWidth = size;

	if (data != NULL)
	{
		bd.Usage = D3D11_USAGE_IMMUTABLE;
		bd.BindFlags = type == QGBUFFER_VERTEX ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER;
		bd.CPUAccessFlags = 0;
		psd = &sd;
		sd.pSysMem = data;
		sd.SysMemPitch = 0;
		sd.SysMemSlicePitch = 0;
	}
	else if (K_OFLAG(flags, QGRESF_DYNAMIC))
	{
		bd.Usage = D3D11_USAGE_DYNAMIC;
		bd.BindFlags = type == QGBUFFER_VERTEX ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER;
		bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		psd = NULL;
	}
	else if (K_OFLAG(flags, QGRESF_LOCAL))
	{
		// 스태이깅 = 예전의 SYSMEM. 
		// : 특정 용도로 바인딩 할 수가 없다.
		// : CPU 플래그가 반드시 지정되어야 한다
		bd.Usage = D3D11_USAGE_STAGING;
		bd.BindFlags = 0;
		bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
		psd = NULL;
	}
	else
	{
		bd.Usage = D3D11_USAGE_DEFAULT;
		bd.BindFlags = type == QGBUFFER_VERTEX ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER;
		bd.CPUAccessFlags = 0;
		psd = NULL;
	}

	if (K_OFLAG(flags, QGRESF_STREAM) && !psd)
		bd.BindFlags |= D3D11_BIND_STREAM_OUTPUT;

	//
	ID3D11Buffer* buf;

	if (FAILED(_device->CreateBuffer(&bd, psd, &buf)))
	{
		base->Unload();

		kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
		return NULL;
	}

	//
	base->_type = (qgBufferType)type;
	base->_flags = flags;
	base->_stride = stride;
	base->_size = size;

	base->_node_desc = K_CAST_PTR_TO_PUINT(buf);

	return base;
}


//////////////////////////////////////////////////////////////////////////
// 레이아웃
KIMPL_SUBCLASS(D11Layout, "D11Layout");

//
D11Layout::D11Layout() throw()
{
}

//
D11Layout::~D11Layout() throw()
{
	if (_node_desc)
	{
		ID3D11InputLayout* il = (ID3D11InputLayout*)_node_desc;
		il->Release();
	}
}

//
bool D11Layout::_BakeShader(D11Shader* shd, ID3D11InputLayout** ret, D11Rdh* dev)
{
	// 실제 만든다...

	// 정점 세이더 확인
	tpl::vector<kbyte>& keep = shd->_bd_vs->keep;

	if (keep.empty())
	{
		// 정점 세이더 코드 데이터가 없다!
		kobj_tracep(KLV_ERROR, KERR_NOTARGET);
		return false;
	}

	//
	ID3D11InputLayout* il;

	HRESULT hr = dev->GetDevice()->CreateInputLayout(
		&(*_descs.begin()), _descs.size(), &(*keep.begin()), keep.size(), &il);

	if (FAILED(hr))
	{
		// 헐... 못 만듦
		kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
		return false;
	}

	// 레이아웃, 세이더 둘다 넣어줌
	_node_desc = (kuintptr)il;

	if (!shd->_node_desc)
	{
		shd->_node_desc = (kuintptr)il;
		il->AddRef();
	}

	if (ret)
		*ret = il;

	return true;
}


//////////////////////////////////////////////////////////////////////////
// 버퍼
KIMPL_SUBCLASS(D11Buffer, "D11Buffer");

//
D11Buffer::D11Buffer() throw()
{
}

//
D11Buffer::~D11Buffer() throw()
{
	if (_node_desc)
	{
		if (_lock)
			D11Buffer::Unmap();

		ID3D11Buffer* buf = (ID3D11Buffer*)_node_desc;
		buf->Release();
	}
}

//
kpointer D11Buffer::Map(kint flags)
{
	// 잠겨 있으면 실패
	if (_lock)
	{
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return NULL;
	}

	// 초기 데이터가 있으면 잠글 수 없음
	if (_bd.Usage == D3D11_USAGE_IMMUTABLE)
	{
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return NULL;
	}

	//
	D11Rdh* rdh = (D11Rdh*)qgRdh::Instance;
	ID3D11DeviceContext* context = rdh->GetContext();
	D3D11_MAPPED_SUBRESOURCE ms;

	//
	if (K_OFLAG(_flags, QGRESF_LOCAL) ||
		((K_OFLAG(_flags, QGRESF_DYNAMIC) && (flags == QGENTF_NONE || flags == QGENTF_WRITE))))
	{
		// 스테이깅이거나 다이나믹이면서 플래그가 없으면 바로 매핑
		ID3D11Buffer* buf = (ID3D11Buffer*)_node_desc;
		D3D11_MAP map;

		if (!K_OFLAG(flags, QGENTF_READ))
			map = D3D11_MAP_WRITE_DISCARD;
		else if (!K_OFLAG(flags, QGENTF_WRITE))
			map = D3D11_MAP_READ;
		else
			map = D3D11_MAP_READ_WRITE;

		if (FAILED(context->Map(buf, 0, map, 0, &ms)))
		{
			kobj_tracep(KLV_ERROR, KERR_MAPPING);
			return NULL;
		}
	}
	else
	{
		// 스태깅 임시 버퍼를 만들어서 반환...
		// 왜냐하면 D11은 스태깅 이외에는 거의 매핑할 수 없어 리소스 자체 복제를 해야함

		// 임시 버퍼 만들고
		D3D11_BUFFER_DESC bd;
		bd.ByteWidth = _bd.ByteWidth;
		bd.Usage = D3D11_USAGE_STAGING;
		bd.BindFlags = 0;
		bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE | D3D11_CPU_ACCESS_READ;
		bd.MiscFlags = 0;
		bd.StructureByteStride = 0;

		if (FAILED(rdh->GetDevice()->CreateBuffer(&bd, NULL, &_stag)))
		{
			kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
			return NULL;
		}

		// 읽기가 가능하면... 읽게 해줘야지... ㅠ_ㅜ
		if (K_OFLAG(flags, QGENTF_READ))
		{
			ID3D11Buffer* buf = (ID3D11Buffer*)_node_desc;
			context->CopyResource(_stag, buf);
		}

		// 임시 스태깅으로 매핑
		if (FAILED(context->Map(_stag, 0, D3D11_MAP_READ_WRITE, 0, &ms)))
		{
			kobj_tracep(KLV_ERROR, KERR_MAPPING);
			return NULL;
		}

		// 까먹지 말고 세팅할 것
		_upload = flags != QGENTF_READ;	// 읽기 전용이 아니면 나중에 반드시 업로드해야함
	}

	//
	_lock = ms.pData;

	return ms.pData;
}

//
bool D11Buffer::Unmap()
{
	if (!_lock)
	{
		// 잠겨있지 않음
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return false;
	}

	D11Rdh* rdh = (D11Rdh*)qgRdh::Instance;
	ID3D11DeviceContext* context = rdh->GetContext();

	ID3D11Buffer* buf = (ID3D11Buffer*)_node_desc;

	if (_stag)
	{
		// 임시 버퍼를 통한 처리
		context->Unmap(_stag, 0);

		if (_upload)
			context->CopyResource(buf, _stag);

		_stag->Release();
		_stag = NULL;;
	}
	else
	{
		// 바로 해제
		context->Unmap(buf, 0);
	}

	//
	_lock = NULL;

	return true;
}

#endif	// _QG_USE_D11

